package com.cn.lotary.util;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 根据实体生成 xhtml和 配置文件 <br/>
 *
 *
 * eg: String viewPath = "/home/user192/deb/view"; <br/>
 *
 * eg: String
 * jarPath="/home/user192/lotary/projects/oa/OA-ejb/dist/OA-ejb.jar";<br/>
 *
 *
 * @author user192
 */
public class PageProvider {

    private static void main(String[] args) throws Exception {
        //createFullView(jarPath, viewPath);
        String jarPath = "/home/user192/lotary/projects/oa/OA-ejb/dist/OA-ejb.jar";
        String newPath = "/home/user192/deb";
        //  createDaoAndController(jarPath, newPath);
    }

    private static String getI18nEn(String str) {
        StringBuilder outcome = new StringBuilder();

        String[] split = str.split("");

        for (int i = 1; i < split.length; i++) {
            String ch = split[i];
            outcome.append(Character.isUpperCase(ch.toCharArray()[0]) ? " " + toLowerCase(ch) : ch);
        }

        return outcome.toString();
    }

    /**
     * 根据实体类生成dao controller xhtml
     *
     * @param jarPath entity jar path
     * @param viewPath new file path
     */
    public static void createAll(String jarPath, String viewPath) {

        try {
            createDaoAndController(jarPath, viewPath);
            createPageView(jarPath, viewPath);
        } catch (Exception e) {
            System.out.println(e.getLocalizedMessage());
        }

    }

    private static void createDaoAndController(String jarPath, String newPath) throws Exception {
        providerClass(jarPath, newPath, ClasType.Dao);
        providerClass(jarPath, newPath, ClasType.Controller);
        providerClass(jarPath, newPath, ClasType.Services);
        providerClass(jarPath, newPath, ClasType.DaoServices);
    }

    /**
     *
     * @param jarPath 包含 实体jar路径
     * @param viewPath 生成 xhtml 路径
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static void createPageView(String jarPath, String viewPath) {

        try {
            viewPath = viewPath + "/view/";
            List<Class> classList = getClassList(jarPath);
            for (Class c : classList) {
                createView(c, viewPath);
            }

            String path = viewPath + "/properties";
            File proFolder = new File(path);
            if (!proFolder.exists()) {
                proFolder.mkdirs();
            }

            String proFilePath = path + "/message.properites";
            File proFile = new File(proFilePath);

            if (proFile.exists()) {
                proFile.renameTo(new File(proFilePath.replace(".properites", "_1.properites")));
            }

            RandomAccessFile file = new RandomAccessFile(proFile, "rw");
            Set<String> entityColumnI18n = getEntityColumnI18n(classList);
            for (String str : entityColumnI18n) {
                file.writeBytes(str + "\r\n");
            }
            file.close();

        } catch (Exception e) {
            System.out.println(e.getLocalizedMessage());
        }
    }

    private static Set<String> getEntityColumnI18n(List<Class> clsss) {
        Set<String> column = new HashSet<String>();
        for (Class c : clsss) {
            for (Field f : c.getDeclaredFields()) {
                String col = f.getName();
                if (col.equals("id") || col.equals("serialVersionUID")) {
                    continue;
                }
                col = col + "=" + getI18nEn(col);
                column.add(col);
            }
        }
        return column;
    }

    private static void createView(Class c, String path) throws IOException {
        makeFile(c, path, PageType.Add);
        makeFile(c, path, PageType.Edit);
        makeFile(c, path, PageType.List);
        makeFile(c, path, PageType.Index);
    }

    /**
     * entity 必 须 在 包 含 entity 的 路 径 下
     *
     * @param jarPath
     * @return entity class
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private static List<Class> getClassList(String jarPath) throws IOException, ClassNotFoundException {
        JarFile j = new JarFile(jarPath);

        URL[] url = new URL[]{new URL("file:" + jarPath)};

        URLClassLoader loader = new URLClassLoader(url);


        List<Class> outcome = new ArrayList<Class>();
        Enumeration<JarEntry> entries = j.entries();

        while (entries.hasMoreElements()) {
            JarEntry nextElement = entries.nextElement();


            if (!nextElement.isDirectory()) {
                String files = nextElement.getName();
                if (files.endsWith(".class")
                        && files.contains("entity")) {
                    String cl = files.replace("/", ".").replace(".class", "");
                    outcome.add(loader.loadClass(cl));
                }
            }
        }

        return outcome;

    }

    private static void makeFile(Class c, String path, PageType cu) throws IOException {
        String dir = path + "/" + toLowerCase(c.getSimpleName());
        File fdir = new File(dir);
        if (!fdir.exists()) {
            fdir.mkdirs();
        }

        String xhtmlPath = dir + "/" + toLowerCase(cu.name()) + ".xhtml";
        File file = new File(xhtmlPath);
        if (!file.exists()) {
            file.createNewFile();
        } else {
            file.renameTo(new File(xhtmlPath.replace(".xhtml", "_j.xhtml")));
        }

        RandomAccessFile xhtml = new RandomAccessFile(file, "rw");
        if (PageType.List.equals(cu)) {

            xhtml.writeBytes(getViewListPage(c));
        } else if (PageType.Index.equals(cu)) {
            xhtml.writeBytes(UI_INDEX);
        } else {
            xhtml.writeBytes(getViewPageAddEdit(c, cu));
        }

        xhtml.close();


    }

    private static void providerClass(String entityJarPath, String newPath, ClasType extension) throws IOException, ClassNotFoundException {
        newPath = newPath + "/class/" + toLowerCase(extension.name());
        File l = new File(newPath);
        if (!l.exists()) {
            l.mkdirs();
        }

        for (Class f : getClassList(entityJarPath)) {
            String entityName = f.getSimpleName();


            String newfullpath = newPath + "/" + entityName + extension.name() + ".java";

            File newfile = new File(newfullpath);
            if (newfile.exists()) {
                newfile.renameTo(new File(newfullpath.replace(".java", "_j.java")));
            }

            FileWriter fileWriter = new FileWriter(newfile, true);
            if (extension.equals(ClasType.Dao)) {
                fileWriter.write(daoClassModel(entityName));
            } else if (extension.equals(ClasType.Controller)) {
                fileWriter.write(controllerClassModel(entityName));
            } else if (extension.equals(ClasType.Services)) {
                fileWriter.write(controllerServicesClassModel(entityName));
            } else if (extension.equals(ClasType.DaoServices)) {
                fileWriter.write(daoServicesClassModel(entityName));
            }

            fileWriter.close();
        }
    }

    private static String getViewListPage(Class c) {
        StringBuilder outcome = new StringBuilder();
        String controller = toLowerCase(c.getSimpleName()) + "Controller";
        outcome.append(UI_START);
        outcome.append("   <h:form>\r\n");
        outcome.append("     <p:dataTable value=\"#{" + controller + ".dataModel}\""
                + " var=\"entity\""
                + " rows=\"10\""
                + " emptyMessage=\"#{viewConfig.emptyMessage}\""
                + " paginatorTemplate=\"#{viewConfig.paginatorTemplate}\""
                + " rowsPerPageTemplate=\"#{viewConfig.rowsPerPageTemplate}\""
                + " paginator=\"true\""
                + " selectionMode=\"single\""
                + " rowIndexVar=\"index\""
                + " resizableColumns=\"true\""
                + " paginatorPosition=\"#{viewConfig.paginatorPosition}\""
                + " rowKey=\"#{entity}\">\r\n");


        outcome.append("           <p:column>\r\n");
        outcome.append("             <f:facet name=\"header\">\r\n");
        outcome.append("               <h:outputText value=\"ID\"/>\r\n");
        outcome.append("             </f:facet>\r\n");
        outcome.append("           <h:outputText value=\"#{index+1}\"/>\r\n");
        outcome.append("           </p:column>\r\n\r\n");
        for (Field f : c.getDeclaredFields()) {
            String fiedldName = f.getName();
            if (fiedldName.equals("id") || fiedldName.equals("serialVersionUID")
                    || f.getType().equals(List.class)) {
                continue;
            } else {

                outcome.append("           <p:column>\r\n");
                outcome.append("             <f:facet name=\"header\">\r\n");
                outcome.append("                <l:outputText value=\"" + fiedldName + "\"/>\r\n");
                outcome.append("             </f:facet>\r\n");
                if (f.getType().equals(Date.class)) {
                    outcome.append("             <h:outputText value=\"#{entity.").append(fiedldName).append("}\">\r\n");
                    outcome.append("                      <f:convertDateTime pattern=\"#{viewConfig.listDateFormat}\"/>\r\n");
                    outcome.append("             </h:outputText>\r\n");

                } else {
                    outcome.append("             <h:outputText value=\"#{entity.").append(fiedldName);
                    //TODO: 得到 实体显示属性
                    outcome.append(isBaseType(f.getType()) ? "" : ".name");
                    outcome.append("}\"/>\r\n");
                    outcome.append(isBaseType(f.getType()) ? "" : "           <!-- TODO: check name -->\r\n");
                }


                outcome.append("           </p:column>\r\n\r\n");
            }
        }

        outcome.append("           <p:column>\r\n");
        outcome.append("             <l:commandButton value=\"delete\"\r\n"
                + " \t\taction=\"#{" + controller + ".remove(entity)}\"\r\n"
                + " \t\tupdate=\"@form,:growl\"\r\n"
                + " \t\tprocess=\"@this\"/>\r\n");
        outcome.append("           </p:column>\r\n");

        outcome.append("           <p:column>\r\n");
        outcome.append(
                "             <l:commandButton action=\"#{" + controller + ".setEntity(entity)}\"\r\n"
                + " \t\t\tvalue=\"edit\"\r\n"
                + " \t\t\tajax=\"false\" \r\n"
                + " \t\t\tpropertyActionListenerRenderd=\"true\"\r\n"
                + " \t\t\tpropertyActionListenerTarget=\"#{viewConfig.contentPage}\"\r\n"
                + " \t\t\tpropertyActionListenerValue=\"edit.xhtml\"/>\r\n");
        outcome.append("           </p:column>\r\n");
        outcome.append("     </p:dataTable>\r\n");
        outcome.append("   </h:form>\r\n");
        outcome.append("</ui:composition>\r\n");

        return outcome.toString();
    }

    private static String getViewPageAddEdit(Class c, PageType cu) {
        String controller = toLowerCase(c.getSimpleName()) + "Controller";
        StringBuilder outcome = new StringBuilder();
        outcome.append(UI_START);
        outcome.append("<h:form id=\"form_").append(toLowerCase(cu.name())).append("\">").append("\r\n");
        outcome.append("\t<p:fieldset legend=\"").append("#{msgs." + toLowerCase(cu.name()) + "}").append("\">").append("\r\n");
        outcome.append("\t\t<h:panelGrid columns=\"3\">").append("\r\n");

        for (Field f : c.getDeclaredFields()) {
            Class type = f.getType();

            if (f.getName().equals("serialVersionUID")
                    || f.getName().equals("id")
                    || f.getName().equals("createTime")
                    || f.getName().equals("addTime")
                    || f.getName().equals("updateTime")) {
                continue;
            }

            String input = "h:inputText";
            String converter = null;
            if (type.equals(Date.class)) {
                input = "p:calendar";
            } else if (type.equals(List.class)) {
                input = "list";
            } else if (type.toString().contains("entity")) {
                input = "h:selectOneMenu";
                converter = toLowerCase(type.getSimpleName()) + "Controller";
            }
            //System.err.println(type);
            String id = toLowerCase(cu.name()) + ObjectUtil.toUpperCase(f.getName());
            if (input.equals("list")) {
                outcome.append("\t\t\t<!--").append("List").append("-->\r\n");
                outcome.append("\t\t\t<!--").append("entitiy.").append(f.getName()).append("-->\r\n");
            } else {
                outcome.append("\t\t\t<l:outputLabel value=\"").append(f.getName()).append("\" for=\"").append(id).append("\"/>").append("\r\n");


                if (input.equals("h:inputText") || input.equals("p:calendar")) {
                    outcome.append("\t\t\t<").append(input).append(" id=\"").append(id).append("\" value=\"#{").append(controller).append(".entity.").append(f.getName()).append("}\"/>").append("\r\n");
                } else {
                    outcome.append("\t\t\t<").append(input).append(" id=\"").append(id).append("\" value=\"#{").append(controller).append(".entity.").append(f.getName()).append("}\" converter=\"#{" + converter + ".converter}\">").append("\r\n");
                    outcome.append("\t\t\t<f:selectItems value=\"#{" + converter + ".selectItems(true)}\"/>").append("\r\n");
                    outcome.append("\t\t\t</h:selectOneMenu>").append("\r\n");
                }

                outcome.append("\t\t\t<p:message for=\"").append(id).append("\"/>").append("\r\n");
            }
            outcome.append("\t\t\r\n");
        }
        if (PageType.Add.equals(cu)) {
            outcome.append("                <l:commandButton value=\"save\"\r\n"
                    + "                                      action=\"#{" + controller + ".create()}\"\r\n"
                    + "                                      update=\"@form,:growl\"/>\r\n");
            outcome.append("                <l:commandButton value=\"cancel\"\r\n"
                    + "                                      action=\"#{" + controller + ".reset()}\"\r\n"
                    + "                                      immediate=\"true\"\r\n"
                    + "                                      process=\"@this\"\r\n"
                    + "                                      update=\"@form\"/>\r\n");
        }

        if (PageType.Edit.equals(cu)) {
            outcome.append("                <l:commandButton value=\"update\"\r\n"
                    + "                                      action=\"#{" + controller + ".update()}\"\r\n"
                    + "                                      ajax=\"false\"\r\n"
                    + "                                      propertyActionListenerRenderd=\"true\"\r\n"
                    + "                                      propertyActionListenerTarget=\"#{viewConfig.contentPage}\"\r\n"
                    + "                                      propertyActionListenerValue=\"list.xhtml\"/>\r\n");

            outcome.append("                <l:commandButton value=\"cancel\"\r\n"
                    + "                                      action=\"#{" + controller + ".reset()}\"\r\n"
                    + "                                      ajax=\"false\"\r\n"
                    + "                                      immediate=\"true\"\r\n"
                    + "                                      propertyActionListenerRenderd=\"true\"\r\n"
                    + "                                      propertyActionListenerTarget=\"#{viewConfig.contentPage}\"\r\n"
                    + "                                      propertyActionListenerValue=\"list.xhtml\"/>\r\n");
        }

        outcome.append("\t\t</h:panelGrid>\r\n");
        outcome.append("\t</p:fieldset>\r\n");
        outcome.append("\t</h:form>\r\n");
        outcome.append("</ui:composition>");
        return outcome.toString();
    }

    /**
     * 首 字 母 转 小 写
     *
     * @param str
     * @return
     */
    private static String toLowerCase(String str) {
        String[] a = str.split("");
        return str.replaceFirst(a[1], a[1].toLowerCase());
    }

    private static boolean isBaseType(Class c) {
        return (c.equals(String.class)
                || c.equals(Integer.class)
                || c.equals(Date.class)
                || c.equals(char.class)
                || c.equals(boolean.class)
                || c.equals(Boolean.class)
                || c.equals(float.class)
                || c.equals(double.class)
                || c.equals(byte.class)
                || c.equals(short.class)
                || c.equals(long.class)
                || c.equals(Long.class)
                || c.equals(int.class));

    }

    private static String daoClassModel(String entityName) {
        String outcome =
                "package com.cn.lotary.dao;\r\n"
                + "\r\n"
                + "import com.cn.lotary.dao.services." + entityName + "DaoServices;\r\n"
                + "import javax.ejb.Stateless;\r\n"
                + "\r\n"
                + "/**\r\n"
                + " *\r\n"
                + " * @author By ClassProvider\r\n"
                + " */\r\n"
                + "@Stateless\r\n"
                + "public class " + entityName + "Dao extends " + entityName + "DaoServices {\r\n"
                + "}\r\n";

        return outcome;
    }

    private static String daoServicesClassModel(String entityname) {
        String outcome =
                "package com.cn.lotary.dao.services;\r\n"
                + "\r\n"
                + "import com.cn.lotary.base.dao.BaseDao;\r\n"
                + "import com.cn.lotary.base.dao.BaseDaoParam;\r\n"
                + "import com.cn.lotary.entity." + entityname + ";\r\n"
                + "import javax.persistence.EntityManager;\r\n"
                + "import javax.persistence.PersistenceContext;\r\n"
                + "\r\n"
                + "/**\r\n"
                + " *\r\n"
                + " * @author By ClassProvider\r\n"
                + " */\r\n"
                + "public class " + entityname + "DaoServices extends BaseDao<" + entityname + "> {\r\n"
                + "\r\n"
                + "    @PersistenceContext\r\n"
                + "    private EntityManager em;\r\n"
                + "\r\n"
                + "    @Override\r\n"
                + "    protected BaseDaoParam getBaseDaoParam() {\r\n"
                + "        return new BaseDaoParam<" + entityname + ">(em, " + entityname + ".class, \"getId\");\r\n"
                + "    }\r\n"
                + "}\r\n";

        return outcome;
    }

    private static String controllerClassModel(String entityname) {
        String outcome =
                "package com.cn.lotary.web.controller;\r\n"
                + "\r\n"
                + "import com.cn.lotary.web.controller.services." + entityname + "Services;\r\n"
                + "import javax.faces.bean.ManagedBean;\r\n"
                + "import javax.faces.bean.ViewScoped;\r\n"
                + "\r\n"
                + "/**\r\n"
                + " *\r\n"
                + " * @author Auto Provider \r\n"
                + " */\r\n"
                + "@ManagedBean\r\n"
                + "@ViewScoped\r\n"
                + "public class " + entityname + "Controller extends " + entityname + "Services  {\r\n"
                + "}";
        return outcome;
    }

    private static String controllerServicesClassModel(String entityName) {
        String outcome =
                "package com.cn.lotary.web.controller.services;\r\n"
                + "\r\n"
                + "import com.cn.lotary.base.web.BaseController;\r\n"
                + "import com.cn.lotary.dao." + entityName + "Dao;\r\n"
                + "import com.cn.lotary.entity." + entityName + ";\r\n"
                + "import javax.ejb.EJB;\r\n"
                + "\r\n"
                + "/**\r\n"
                + " *\r\n"
                + " * @author Auto Provider \r\n"
                + " */\r\n"
                + "public class " + entityName + "Services extends BaseController<" + entityName + "> {\r\n"
                + "\r\n"
                + "    @EJB\r\n"
                + "    private " + entityName + "Dao dao;\r\n"
                + "\r\n"
                + "    @Override\r\n"
                + "    protected " + entityName + "Dao dao() {\r\n"
                + "        return dao;\r\n"
                + "    }\r\n"
                + "\r\n"
                + "    @Override\r\n"
                + "    protected String selectItemLabel() {\r\n"
                + "          //TODO:check return value\r\n"
                + "          return \"getName\";\r\n"
                + "    }\r\n"
                + "}\r\n";
        return outcome;
    }
    private static final String UI_START =
            "<ui:composition\r\n"
            + "     xmlns=\"http://www.w3.org/1999/xhtml\"\r\n"
            + "     xmlns:p=\"http://primefaces.org/ui\"\r\n"
            + "     xmlns:h=\"http://java.sun.com/jsf/html\"\r\n"
            + "     xmlns:ui=\"http://java.sun.com/jsf/facelets\"\r\n"
            + "     xmlns:l=\"http://java.sun.com/jsf/composite/l\"\r\n"
            + "     xmlns:f=\"http://java.sun.com/jsf/core\">\r\n"
            + "<h:head></h:head>\r\n";
    private static final String UI_INDEX =
            "<ui:composition xmlns:ui=\"http://java.sun.com/jsf/facelets\"\r\n"
            + "                template=\"/template/culTemplate.xhtml\">\r\n"
            + "    <ui:define name=\"main_includ\">\r\n"
            + "        <ui:include src=\"#{viewConfig.contentPage}\"/>\r\n"
            + "    </ui:define>\r\n"
            + "</ui:composition>";
}

enum PageType {

    Add, Edit, List, Index;
}

enum ClasType {

    Services, Controller, Dao, DaoServices;
}
